package ed25519

import (
	"crypto/ed25519"
	"crypto/rand"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"gitee.com/zackeus/go-boot/tools/errorx"
)

// GenerateEdDSA 生成 EdDSA 密钥对
func GenerateEdDSA() (ed25519.PrivateKey, ed25519.PublicKey, error) {
	pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		return nil, nil, errorx.Wrap(err, "生成 EdDSA 密钥失败")
	}

	return privKey, pubKey, nil
}

// GenerateEdDSAPem 生成 EdDSA 密钥对
func GenerateEdDSAPem() ([]byte, []byte, error) {
	privKey, pubKey, err := GenerateEdDSA()
	if err != nil {
		return nil, nil, err
	}

	/* 编码私钥 */
	privBytes, err := x509.MarshalPKCS8PrivateKey(privKey)
	if err != nil {
		return nil, nil, errorx.Wrap(err, "编码 EdDSA 私钥失败")
	}
	privPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "PRIVATE KEY",
		Bytes: privBytes,
	})

	/* 编码公钥 */
	pubBytes, err := x509.MarshalPKIXPublicKey(pubKey)
	if err != nil {
		return nil, nil, errorx.Wrap(err, "编码 EdDSA 公钥失败")
	}
	pubPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: pubBytes,
	})

	return privPEM, pubPEM, nil
}

// LoadEdDSAPrivateKey 从 PEM 格式的字节切片中加载 EdDSA 私钥
// pemBytes 是由 GenerateEdDSAPem 函数生成的私钥字节切片
func LoadEdDSAPrivateKey(pemBytes []byte) (ed25519.PrivateKey, error) {
	// 1. 解码 PEM 块
	block, _ := pem.Decode(pemBytes)
	if block == nil {
		return nil, errors.New("解码 PEM 私钥失败，未找到有效的 PEM 块")
	}

	// 2. 检查 PEM 块类型是否为私钥
	if block.Type != "PRIVATE KEY" {
		return nil, errors.New(fmt.Sprintf("无效的 PEM 块类型: %s, 应为 PRIVATE KEY", block.Type))
	}

	// 3. 使用 x509 解析 PKCS#8 格式的私钥
	// 注意：ed25519 私钥在 Go 中使用 PKCS#8 格式进行编码和解码
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return nil, errorx.Wrap(err, "解析 PKCS#8 私钥失败")
	}

	// 4. 将解析出的通用 key 断言为 ed25519.PrivateKey 类型
	privKey, ok := key.(ed25519.PrivateKey)
	if !ok {
		return nil, errors.New("解析出的私钥不是 ed25519.PrivateKey 类型")
	}
	return privKey, nil
}

// LoadEdDSAPublicKey 从 PEM 格式的字节切片中加载 EdDSA 公钥
// pemBytes 是由 GenerateEdDSAPem 函数生成的公钥字节切片
func LoadEdDSAPublicKey(pemBytes []byte) (ed25519.PublicKey, error) {
	// 1. 解码 PEM 块
	block, _ := pem.Decode(pemBytes)
	if block == nil {
		return nil, errors.New("解码 PEM 公钥失败，未找到有效的 PEM 块")
	}

	// 2. 检查 PEM 块类型是否为公钥
	if block.Type != "PUBLIC KEY" {
		return nil, errors.New(fmt.Sprintf("无效的 PEM 块类型: %s, 应为 PUBLIC KEY", block.Type))
	}

	// 3. 使用 x509 解析 PKIX 格式的公钥
	key, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, errorx.Wrap(err, "解析 PKIX 公钥失败")
	}

	// 4. 将解析出的通用 key 断言为 ed25519.PublicKey 类型
	pubKey, ok := key.(ed25519.PublicKey)
	if !ok {
		return nil, errors.New("解析出的公钥不是 ed25519.PublicKey 类型")
	}

	return pubKey, nil
}
